﻿using log4net;
using log4net.Config;
using log4net.Repository;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;

namespace VideoPlayer.Utils
{
    public class Logger
    {
        private static ILog logger;

        private static int ifConfiged;

        private static void Init()
        {
            ILoggerRepository loggerRepository = (Interlocked.Exchange(ref ifConfiged, 1) != 0) ? LogManager.GetRepository("NETRepository") : LogManager.CreateRepository("NETRepository");
            if (File.Exists(LoggerConfig.ConfigPath))
            {
                XmlConfigurator.Configure(loggerRepository, new FileInfo(LoggerConfig.ConfigPath));
            }
            else if (LoggerConfig.XmlElement != null)
            {
                XmlConfigurator.Configure(loggerRepository, LoggerConfig.XmlElement);
            }

            logger = LogManager.GetLogger(loggerRepository.Name, "InfoLogger");
        }

        public static void Info(string sMessage, Exception ex = null)
        {
            Init();
            AddStackTrace(ref sMessage);
            if (ex == null)
            {
                logger.Info(sMessage);
            }
            else
            {
                logger.Info(sMessage, ex);
            }
        }

        public static void Debug(string sMessage, Exception ex = null)
        {
            Init();
            AddStackTrace(ref sMessage);
            if (ex == null)
            {
                logger.Debug(sMessage);
            }
            else
            {
                logger.Debug(sMessage, ex);
            }
        }

        public static void Warn(string sMessage, Exception ex = null)
        {
            Init();
            AddStackTrace(ref sMessage);
            if (ex == null)
            {
                logger.Warn(sMessage);
            }
            else
            {
                logger.Warn(sMessage, ex);
            }
        }

        public static void Error(string sMessage, Exception ex = null)
        {
            Init();
            AddStackTrace(ref sMessage);
            if (ex == null)
            {
                logger.Error(sMessage);
            }
            else
            {
                logger.Error(sMessage, ex);
            }
        }

        public static void Fatal(string sMessage, Exception ex = null)
        {
            Init();
            AddStackTrace(ref sMessage);
            if (ex == null)
            {
                logger.Fatal(sMessage);
            }
            else
            {
                logger.Fatal(sMessage, ex);
            }
        }

        private static void AddStackTrace(ref string sMessage)
        {
            if (logger.IsDebugEnabled)
            {
                sMessage = StackTraceHelper.GetStackTraceInfo() + Environment.NewLine + sMessage;
            }
        }
    }

    internal class StackTraceHelper
    {
        private const string _FilterdName = "AddStackTrace,Debug,Info,Warn,Error,DebuggableCallback,WndProc,OnMessage,WmMouseUp,OnMouseUp,OnClick";

        internal static string GetStackTraceInfo()
        {
            StackFrame[] frames = new StackTrace().GetFrames();
            StringBuilder stringBuilder = new StringBuilder(200);
            for (int i = 3; i < frames.Length && -1 != frames[i].GetILOffset(); i++)
            {
                MethodBase method = frames[i].GetMethod();
                Type declaringType = method.DeclaringType;
                if (!"AddStackTrace,Debug,Info,Warn,Error,DebuggableCallback,WndProc,OnMessage,WmMouseUp,OnMouseUp,OnClick".Contains(method.Name))
                {
                    stringBuilder.Append(declaringType.FullName + "." + method.Name + "->");
                }
            }

            frames = null;
            return stringBuilder.ToString().TrimEnd('-', '>');
        }
    }

    public class LoggerConfig
    {
        private static object pathLock = new object();

        private static string configPath = "Log4Net.config";

        private static XmlElement xmlElement = null;

        public static string ConfigPath
        {
            internal get
            {
                return configPath;
            }
            set
            {
                lock (pathLock)
                {
                    configPath = value;
                }
            }
        }

        internal static XmlElement XmlElement => xmlElement;

        public static void SetConfig(string path)
        {
            lock (pathLock)
            {
                configPath = path;
            }
        }

        public static void SetConfig(XmlElement param)
        {
            xmlElement = param;
        }
    }
}

